﻿using Microsoft.Owin.Security;

using Sustainalytics.FileManager;
using Sustainalytics.LongRunningTaskManager.Interface;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using Xunit;

namespace Sustainalytics.LongRunningTaskManager.Tests
{

    public class LongRunningTaskManagerTests
    {
        [Fact]
        public void Init_Success()
        {
            var taskInstanceSnapshotRepo = A.Fake<ILongRunningTaskInstanceSnapshotRepository>();
            {
                GetAll = () =>
                    {
                        List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();
                        return lst;
                    }
            };

            var taskManager = CreateTaskManager(taskInstanceSnapshotRepo);
        }

        private void StopManager(ILongRunningTaskManager taskManager)
        {
            try
            {
                taskManager.StopManager().Wait();
            }
            catch (OperationCanceledException)
            {
                // cancellation
            }
            catch (AggregateException ex)
            {
                if (ex.InnerExceptions.Any(e => e is OperationCanceledException))
                {
                    // cancellation
                }
                else
                {
                    throw ex;
                }
            }
        }

        #region Start Task

        [Fact]
        [ExpectedException(typeof(LongRunningTaskManagerAuthenticationTicketNullException))]
        public void StartTask_AuthenticationTicketNull_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            taskManager.Start(
                id: null,
                title: "myTask",
                ticket: null,
                paramJson: null,
                taskType: new MyTaskInt().Type,
                order: null,
                isUpload: false,
                isDownload: false);
        }

        [Fact]
        public void StartTask_Success()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string paramJson = new MyTaskParam { DelayMs = 0, Data = "abc" }.ToJson();

            LongRunningTaskTypeEnum type = new MyTaskInt().Type;

            var ticket = CreateAuthenticationTicket("myUser");

            // start task
            string taskInstanceId = taskManager.Start(
                id: null,
                title: "myTask",
                ticket: ticket,
                paramJson: paramJson,
                taskType: type,
                isUpload: false,
                isDownload: false,
                order: null);

            Assert.IsNotNull(taskInstanceId, "Task instance ID is null.");
            Assert.IsTrue(lst.Count == 1, "Repository has invalid state.");

            taskManager.StopManagerAndWait();
        }

        [Fact]
        [ExpectedException(typeof(LongRunningMaxTasksPerUserReachedException))]
        public void StartTask_MaxTasksPerUserReached_Success()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);


            string title1 = "myTask1";
            var taskInstanceId1 = taskManager.Start(
                id: null,
                title: title1,
                ticket: ticket,
                paramJson: null,
                taskType: new MyTaskInt().Type,
                isUpload: false,
                isDownload: false,
                order: null);

            string title2 = "myTask2";
            var taskInstanceId2 = taskManager.Start(
                id: null,
                title: title2,
                ticket: ticket,
                paramJson: null,
                taskType: new MyTaskInt().Type,
                isUpload: false,
                isDownload: false,
                order: null);

            string title3 = "myTask3";
            var taskInstanceId3 = taskManager.Start(
                id: null,
                title: title3,
                ticket: ticket,
                paramJson: null,
                taskType: new MyTaskInt().Type,
                isUpload: false,
                isDownload: false,
                order: null);

            string title4 = "myTask4";
            var taskInstanceId4 = taskManager.Start(
                id: null,
                title: title4,
                ticket: ticket,
                paramJson: null,
                taskType: new MyTaskInt().Type,
                isUpload: false,
                isDownload: false,
                order: null);
        }

        #endregion


        #region GetStatus

        [Fact]
        [ExpectedException(typeof(LongRunningTaskManagerAuthenticationTicketNullException))]
        public void GetStatus_AuthenticationTicketNull_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            taskManager.GetStatus(null, "abc");
        }

        [Fact]
        [ExpectedException(typeof(LongRunningTaskInstanceIdNullOrEmptyException))]
        public void GetStatus_TaskInstanceIdNull_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            var ticket = CreateAuthenticationTicket("myUser");

            taskManager.GetStatus(ticket, null);
        }

        [Fact]
        [ExpectedException(typeof(LongRunningTaskInstanceNotFoundException))]
        public void GetStatus_NotFound_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            var ticket = CreateAuthenticationTicket("myUser");

            taskManager.GetStatus(ticket, "abc");
        }

        [Fact]
        public void GetStatus_Running_Success()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string paramJson = new MyTaskParam { DelayMs = 300, Data = "abc" }.ToJson();

            LongRunningTaskTypeEnum type = new MyTaskInt().Type;

            var ticket = CreateAuthenticationTicket("myUser");

            var title = "myTask";

            // start task
            string taskInstanceId = taskManager.Start(
                id: null,
                title: title,
                ticket: ticket,
                paramJson: paramJson,
                taskType: type,
                isUpload: false,
                isDownload: false,
                order: null);

            Assert.IsNotNull(taskInstanceId, "Task instance ID is null.");
            Assert.IsTrue(lst.Count == 1, "Repository has invalid state.");

            //Thread.Sleep(100);
            var asyncInfo = taskManager.GetTaskAsyncInfo(ticket, taskInstanceId);
            asyncInfo.WaitForStatus(LongRunningTaskInstanceStatusEnum.Running, 2000);

            // get task status
            var taskInstanceStatus = taskManager.GetStatus(ticket, taskInstanceId);

            Assert.IsNotNull(taskInstanceStatus, "Task instance status is null.");
            Assert.IsTrue(taskInstanceStatus.TaskInstanceStatus == LongRunningTaskInstanceStatusEnum.Running, "Task is not running");
            Assert.IsTrue(taskInstanceStatus.Type == type, "Invalid task type.");
            Assert.IsTrue(taskInstanceStatus.Title == title, "Invalid title.");
            Assert.IsTrue(taskInstanceStatus.TaskStatus.Progress == null, "Invalid progress");

            taskManager.StopManagerAndWait();
        }

        [Fact]
        public void GetStatus_Finished_Success()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string paramJson = new MyTaskParam { DelayMs = 0, Data = "abc" }.ToJson();

            LongRunningTaskTypeEnum type = new MyTaskInt().Type;

            var ticket = CreateAuthenticationTicket("myUser");

            var title = "myTask";

            // start task
            string taskInstanceId = taskManager.Start(
                id: null,
                title: title,
                ticket: ticket,
                paramJson: paramJson,
                taskType: type,
                isUpload: false,
                isDownload: false,
                order: null);

            Assert.IsNotNull(taskInstanceId, "Task instance ID is null.");
            Assert.IsTrue(lst.Count == 1, "Repository has invalid state.");

            //Thread.Sleep(100);
            var asyncInfo = taskManager.GetTaskAsyncInfo(ticket, taskInstanceId);
            asyncInfo.WaitForStatus(LongRunningTaskInstanceStatusEnum.Finished, 2000);

            // get task status
            var taskInstanceStatus = taskManager.GetStatus(ticket, taskInstanceId);

            Assert.IsNotNull(taskInstanceStatus, "Task instance status is null.");
            Assert.IsTrue(taskInstanceStatus.TaskInstanceStatus == LongRunningTaskInstanceStatusEnum.Finished, "Task is not running instead is "
                + taskInstanceStatus.TaskInstanceStatus.ToString());
            Assert.IsTrue(taskInstanceStatus.Type == type, "Invalid task type.");
            Assert.IsTrue(taskInstanceStatus.Title == title, "Invalid title.");
            Assert.IsTrue(taskInstanceStatus.TaskStatus.Progress == null, "Invalid progress");

            taskManager.StopManagerAndWait();
        }

        [Fact]
        public void GetStatus_Cancelled_Success()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string paramJson = new MyTaskParam { DelayMs = 3000, Data = "abc" }.ToJson();

            LongRunningTaskTypeEnum type = new MyTaskInt().Type;

            var ticket = CreateAuthenticationTicket("myUser");

            var title = "myTask";

            // start task
            string taskInstanceId = taskManager.Start(
                id: null,
                title: title,
                ticket: ticket,
                paramJson: paramJson,
                taskType: type,
                isUpload: false,
                isDownload: false,
                order: null);

            Assert.IsNotNull(taskInstanceId, "Task instance ID is null.");
            Assert.IsTrue(lst.Count == 1, "Repository has invalid state.");

            Thread.Sleep(100);

            taskManager.CancelAndWait(ticket, taskInstanceId);

            Thread.Sleep(100);

            // get task status
            var taskInstanceStatus = taskManager.GetStatus(ticket, taskInstanceId);

            Assert.IsNotNull(taskInstanceStatus, "Task instance status is null.");
            Assert.IsTrue(taskInstanceStatus.TaskInstanceStatus == LongRunningTaskInstanceStatusEnum.Cancelled, "Task is not running");
            Assert.IsTrue(taskInstanceStatus.Type == type, "Invalid task type.");
            Assert.IsTrue(taskInstanceStatus.Title == title, "Invalid title.");
            Assert.IsTrue(taskInstanceStatus.TaskStatus.Progress == null, "Invalid progress");

            taskManager.StopManagerAndWait();
        }

        [Fact]
        public void GetStatus_Failed_UnhandledException_Success()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string paramJson = new MyTaskParam { DelayMs = 0, Data = "abc" }.ToJson();

            LongRunningTaskTypeEnum type = new MyTaskUnknownError().Type;

            var ticket = CreateAuthenticationTicket("myUser");

            var title = "myTask";

            // start task
            string taskInstanceId = taskManager.Start(
                id: null,
                title: title,
                ticket: ticket,
                paramJson: paramJson,
                taskType: type,
                isUpload: false,
                isDownload: false,
                order: null);

            Assert.IsNotNull(taskInstanceId, "Task instance ID is null.");
            Assert.IsTrue(lst.Count == 1, "Repository has invalid state.");

            //Thread.Sleep(100);
            var asyncInfo = taskManager.GetTaskAsyncInfo(ticket, taskInstanceId);
            asyncInfo.WaitForStatus(LongRunningTaskInstanceStatusEnum.Failed, 2000);

            // get task status
            var taskInstanceStatus = taskManager.GetStatus(ticket, taskInstanceId);

            Assert.IsNotNull(taskInstanceStatus, "Task instance status is null.");
            Assert.IsTrue(taskInstanceStatus.TaskInstanceStatus == LongRunningTaskInstanceStatusEnum.Failed, "Task is not failed instead is "
                + taskInstanceStatus.TaskInstanceStatus.ToString());
            Assert.IsTrue(taskInstanceStatus.TaskStatus.Errors != null && taskInstanceStatus.TaskStatus.Errors.Count() == 0, "Invalid error list.");
            Assert.IsTrue(taskInstanceStatus.Type == type, "Invalid task type.");
            Assert.IsTrue(taskInstanceStatus.Title == title, "Invalid title.");
            Assert.IsTrue(taskInstanceStatus.TaskStatus.Progress == null, "Invalid progress");

            taskManager.StopManagerAndWait();
        }

        [Fact]
        public void GetStatus_Failed_TaskErrors_Success()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            var param = new MyTaskParam
            {
                DelayMs = 0,
                Data = "abc",
                Errors = new List<LongRunningTaskError>
                {
                    new LongRunningTaskError(5),
                    new LongRunningTaskError(7),
                    new LongRunningTaskError(3),
                }
            };

            string paramJson = param.ToJson();

            LongRunningTaskTypeEnum type = new MyTaskInt().Type;

            var ticket = CreateAuthenticationTicket("myUser");

            var title = "myTask";

            // start task
            string taskInstanceId = taskManager.Start(
                id: null,
                title: title,
                ticket: ticket,
                paramJson: paramJson,
                taskType: type,
                isUpload: false,
                isDownload: false,
                order: null);

            Assert.IsNotNull(taskInstanceId, "Task instance ID is null.");
            Assert.IsTrue(lst.Count == 1, "Repository has invalid state.");

            //Thread.Sleep(100);
            var asyncInfo = taskManager.GetTaskAsyncInfo(ticket, taskInstanceId);
            asyncInfo.WaitForStatus(LongRunningTaskInstanceStatusEnum.Failed, 2000);

            // get task status
            var taskInstanceStatus = taskManager.GetStatus(ticket, taskInstanceId);

            Assert.IsNotNull(taskInstanceStatus, "Task instance status is null.");
            Assert.IsTrue(taskInstanceStatus.TaskInstanceStatus == LongRunningTaskInstanceStatusEnum.Failed, "Task is not failed");
            Assert.IsTrue(taskInstanceStatus.Type == type, "Invalid task type.");
            Assert.IsTrue(taskInstanceStatus.Title == title, "Invalid title.");
            Assert.IsTrue(taskInstanceStatus.TaskStatus.Progress == null, "Invalid progress");

            Assert.IsTrue(taskInstanceStatus.TaskStatus.Errors != null
                && taskInstanceStatus.TaskStatus.Errors.OrderBy(el => el.ErrorCode).SequenceEqual(param.Errors.OrderBy(el => el.ErrorCode), new LongRunningTaskError()));

            taskManager.StopManagerAndWait();
        }

        [Fact]
        public void GetStatus_Progress_Success()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            var param = new MyTaskParam { DelayMs = 1000, Data = "abc", Progress = 70.5f };

            string paramJson = param.ToJson();

            LongRunningTaskTypeEnum type = new MyTaskInt().Type;

            var ticket = CreateAuthenticationTicket("myUser");

            var title = "myTask";

            // start task
            string taskInstanceId = taskManager.Start(
                id: null,
                title: title,
                ticket: ticket,
                paramJson: paramJson,
                taskType: type,
                isUpload: false,
                isDownload: false,
                order: null);

            Assert.IsNotNull(taskInstanceId, "Task instance ID is null.");
            Assert.IsTrue(lst.Count == 1, "Repository has invalid state.");

            var asyncInfo = taskManager.GetTaskAsyncInfo(ticket, taskInstanceId);
            asyncInfo.WaitForStatus(LongRunningTaskInstanceStatusEnum.Running, 2000);

            // wait for the status to be set
            Thread.Sleep(200);

            // get task status
            var taskInstanceStatus = taskManager.GetStatus(ticket, taskInstanceId);

            Assert.IsNotNull(taskInstanceStatus, "Task instance status is null.");
            Assert.IsTrue(taskInstanceStatus.TaskInstanceStatus == LongRunningTaskInstanceStatusEnum.Running, "Task is not running instead is "
                + taskInstanceStatus.TaskInstanceStatus.ToString());
            Assert.IsTrue(taskInstanceStatus.Type == type, "Invalid task type.");
            Assert.IsTrue(taskInstanceStatus.Title == title, "Invalid title.");
            Assert.IsTrue(taskInstanceStatus.TaskStatus.Progress == param.Progress, "Invalid progress");

            taskManager.StopManagerAndWait();
        }

        #endregion


        #region GetOverallStatus

        [Fact]
        [ExpectedException(typeof(LongRunningTaskManagerAuthenticationTicketNullException))]
        public void GetOverallStatus_AuthenticationTicketNull_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            taskManager.GetOverallStatus(null);
        }

        [Fact]
        public void GetOverallStatus_Success()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);


            string title1 = "myTask1";
            var taskInstanceId1 = taskManager.Start(
                id: null,
                title: title1,
                ticket: ticket,
                paramJson: null,
                taskType: new MyTaskInt().Type,
                isUpload: false,
                isDownload: false,
                order: null);

            string title2 = "myTask2";
            var taskInstanceId2 = taskManager.Start(
                id: null,
                title: title2,
                ticket: ticket,
                paramJson: null,
                taskType: new MyTaskInt().Type,
                isUpload: false,
                isDownload: false,
                order: null);

            string title3 = "myTask3";
            var taskInstanceId3 = taskManager.Start(
                id: null,
                title: title3,
                ticket: ticket,
                paramJson: null,
                taskType: new MyTaskInt().Type,
                isUpload: false,
                isDownload: false,
                order: null);


            var taskInstanceStatusLst = taskManager.GetOverallStatus(ticket);

            Assert.IsNotNull(taskInstanceStatusLst, "Task instance statuses list is null.");
            Assert.IsTrue(taskInstanceStatusLst.Count == 3, "Invalid number of task instance statuses.");

            taskManager.StopManagerAndWait();
        }

        #endregion


        #region GetResult

        [Fact]
        [ExpectedException(typeof(LongRunningTaskManagerAuthenticationTicketNullException))]
        public void GetResult_AuthenticationTicketNull_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            taskManager.GetResult(null, "abc");
        }

        [Fact]
        [ExpectedException(typeof(LongRunningTaskInstanceIdNullOrEmptyException))]
        public void GetResult_TaskInstanceIdNull_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            taskManager.GetResult(ticket, null);
        }

        [Fact]
        [ExpectedException(typeof(LongRunningTaskInstanceNotFoundException))]
        public void GetResult_TaskInstanceIdNotFound_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            taskManager.GetResult(ticket, "abc");
        }

        [Fact]
        public void GetResult_OjectToObjectResult_Success()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            string title = "myTask";

            var param = new MyTaskParam
            {
            };

            string paramJson = param.ToJson();

            LongRunningTaskTypeEnum type = new MyTaskInt().Type;

            string taskInstanceId = taskManager.Start(
                id: null,
                title: title,
                ticket: ticket,
                paramJson: paramJson,
                taskType: type,
                isUpload: false,
                isDownload: false,
                order: null);

            //Thread.Sleep(100);
            var asyncInfo = taskManager.GetTaskAsyncInfo(ticket, taskInstanceId);
            asyncInfo.WaitForStatus(LongRunningTaskInstanceStatusEnum.Finished, 2000);

            var result = taskManager.GetResult(ticket, taskInstanceId);

            Assert.IsNotNull(result, "Result is null.");
            Assert.IsTrue(result.Type == LongRunningTaskResultTypeEnum.Object, "Result type is invalid.");
            Assert.IsTrue((int)((LongRunningTaskObjectResult)result).Data == MyTaskInt.ReturnValue, "Return data is invalid");

            taskManager.StopManagerAndWait();
        }

        [Fact]
        public void GetResult_ObjectResult_Success()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            string title = "myTask";

            var param = new MyTaskParam
            {
            };

            string paramJson = param.ToJson();

            LongRunningTaskTypeEnum type = new MyTaskObject().Type;

            string taskInstanceId = taskManager.Start(
                id: null,
                title: title,
                ticket: ticket,
                paramJson: paramJson,
                taskType: type,
                isUpload: false,
                isDownload: false,
                order: null);

            var asyncInfo = taskManager.GetTaskAsyncInfo(ticket, taskInstanceId);
            asyncInfo.WaitForStatus(LongRunningTaskInstanceStatusEnum.Finished, 2000);

            var result = taskManager.GetResult(ticket, taskInstanceId);

            Assert.IsNotNull(result, "Result is null.");
            Assert.IsTrue(result.Type == LongRunningTaskResultTypeEnum.Object, "Result type is invalid.");
            Assert.IsTrue((int)((LongRunningTaskObjectResult)result).Data == (int)MyTaskObject.ReturnValue.Data, "Return data is invalid");

            taskManager.StopManagerAndWait();
        }

        [Fact]
        public void GetResult_FileOnDiskToFileManagerResult_Success()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            string title = "myTask";

            var param = new MyTaskParam
            {
            };

            string paramJson = param.ToJson();

            LongRunningTaskTypeEnum type = new MyTaskFileOnDisk().Type;

            string taskInstanceId = taskManager.Start(
                id: null,
                title: title,
                ticket: ticket,
                paramJson: paramJson,
                taskType: type,
                isUpload: false,
                isDownload: false,
                order: null);

            var asyncInfo = taskManager.GetTaskAsyncInfo(ticket, taskInstanceId);
            asyncInfo.WaitForStatus(LongRunningTaskInstanceStatusEnum.Finished, 2000);

            var result = taskManager.GetResult(ticket, taskInstanceId);

            Assert.IsNotNull(result, "Result is null.");
            Assert.IsTrue(result.Type == LongRunningTaskResultTypeEnum.FileManager, "Result type is invalid.");

            LongRunningTaskFileManagerResult fileMgrRes = result as LongRunningTaskFileManagerResult;

            Assert.IsTrue(fileMgrRes.FilePath == MyTaskFileManager.ReturnValue.FilePath);

            taskManager.StopManagerAndWait();
        }

        [Fact]
        public void GetResult_FileInMemToFileManagerResult_Success()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            string title = "myTask";

            var param = new MyTaskParam
            {
            };

            string paramJson = param.ToJson();

            LongRunningTaskTypeEnum type = new MyTaskFileInMem().Type;

            var expectedRes = MyTaskFileInMem.TransformedValue;

            if (File.Exists(expectedRes.FilePath))
            {
                File.Delete(expectedRes.FilePath);
            }

            string taskInstanceId = taskManager.Start(
                id: null,
                title: title,
                ticket: ticket,
                paramJson: paramJson,
                taskType: type,
                isUpload: false,
                isDownload: false,
                order: null);

            //Thread.Sleep(100);
            var asyncInfo = taskManager.GetTaskAsyncInfo(ticket, taskInstanceId);
            asyncInfo.WaitForStatus(LongRunningTaskInstanceStatusEnum.Finished, 2000);

            var result = taskManager.GetResult(ticket, taskInstanceId);

            Assert.IsNotNull(result, "Result is null.");
            Assert.IsTrue(result.Type == LongRunningTaskResultTypeEnum.FileManager, "Result type is invalid.");

            LongRunningTaskFileManagerResult fileMgrRes = result as LongRunningTaskFileManagerResult;

            Assert.IsTrue(fileMgrRes.FilePath == MyTaskFileManager.ReturnValue.FilePath);

            taskManager.StopManagerAndWait();
        }

        [Fact]
        public void GetResult_FileManagerResult_Success()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            string title = "myTask";

            var param = new MyTaskParam
            {
            };

            string paramJson = param.ToJson();

            LongRunningTaskTypeEnum type = new MyTaskFileManager().Type;

            string taskInstanceId = taskManager.Start(
                id: null,
                title: title,
                ticket: ticket,
                paramJson: paramJson,
                taskType: type,
                isUpload: false,
                isDownload: false,
                order: null);

            //Thread.Sleep(100);
            var asyncInfo = taskManager.GetTaskAsyncInfo(ticket, taskInstanceId);
            asyncInfo.WaitForStatus(LongRunningTaskInstanceStatusEnum.Finished, 2000);

            var result = taskManager.GetResult(ticket, taskInstanceId);

            Assert.IsNotNull(result, "Result is null.");
            Assert.IsTrue(result.Type == LongRunningTaskResultTypeEnum.FileManager, "Result type is invalid.");

            LongRunningTaskFileManagerResult fileMgrRes = result as LongRunningTaskFileManagerResult;

            Assert.IsTrue(fileMgrRes.FilePath == MyTaskFileManager.ReturnValue.FilePath);

            taskManager.StopManagerAndWait();
        }

        #endregion


        #region Cancel

        [Fact]
        [ExpectedException(typeof(LongRunningTaskManagerAuthenticationTicketNullException))]
        public void Cancel_AuthenticationTicketNull_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            taskManager.CancelAndWait(null, "abc");
        }

        [Fact]
        [ExpectedException(typeof(LongRunningTaskInstanceIdNullOrEmptyException))]
        public void Cancel_TaskInstanceIdNull_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            taskManager.CancelAndWait(ticket, null);
        }

        [Fact]
        [ExpectedException(typeof(LongRunningTaskInstanceNotFoundException))]
        public void Cancel_TaskInstanceIdNotFound_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            taskManager.CancelAndWait(ticket, "abc");
        }

        [Fact]
        public void Cancel_Success()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            string title = "myTask";

            LongRunningTaskTypeEnum type = new MyTaskInt().Type;

            MyTaskParam param = new MyTaskParam
            {
                DelayMs = 1000
            };

            string paramJson = param.ToJson();

            string taskInstanceId = taskManager.Start(
                id: null,
                title: title,
                ticket: ticket,
                paramJson: paramJson,
                taskType: type,
                isUpload: false,
                isDownload: false,
                order: null);

            var asyncInfo = taskManager.GetTaskAsyncInfo(ticket, taskInstanceId);
            asyncInfo.WaitForStatus(LongRunningTaskInstanceStatusEnum.Running, 2000);

            // get status
            var status = taskManager.GetStatus(ticket, taskInstanceId);

            Assert.IsTrue(status.TaskInstanceStatus == LongRunningTaskInstanceStatusEnum.Running, "Task is not running instead is "
                + status.TaskInstanceStatus.ToString() + " - managed task: " + asyncInfo.ManagedTask.Status.ToString());

            // cancel task
            taskManager.CancelAndWait(ticket, taskInstanceId);

            // get status
            status = taskManager.GetStatus(ticket, taskInstanceId);

            Assert.IsTrue(status.TaskInstanceStatus == LongRunningTaskInstanceStatusEnum.Cancelled, "Task is not cancelled instead is "
                + status.TaskInstanceStatus.ToString() + " - managed task: " + asyncInfo.ManagedTask.Status.ToString());

            taskManager.StopManagerAndWait();
        }

        #endregion


        #region Remove

        [Fact]
        [ExpectedException(typeof(LongRunningTaskManagerAuthenticationTicketNullException))]
        public void Remove_AuthenticationTicketNull_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            taskManager.Remove(null, "abc");
        }

        [Fact]
        [ExpectedException(typeof(LongRunningTaskInstanceIdNullOrEmptyException))]
        public void Remove_TaskInstanceIdNull_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            taskManager.Remove(ticket, null);
        }

        [Fact]
        [ExpectedException(typeof(LongRunningTaskInstanceNotFoundException))]
        public void Remove_TaskInstanceIdNotFound_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            taskManager.Remove(ticket, "abc");
        }

        [Fact]
        public void Remove_Success()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            string title = "myTask";

            LongRunningTaskTypeEnum type = new MyTaskInt().Type;

            MyTaskParam param = new MyTaskParam
            {
                DelayMs = 1000
            };

            string paramJson = param.ToJson();

            string taskInstanceId = taskManager.Start(
                id: null,
                title: title,
                ticket: ticket,
                paramJson: paramJson,
                taskType: type,
                isUpload: false,
                isDownload: false,
                order: null);

            //Thread.Sleep(100);
            var asyncInfo = taskManager.GetTaskAsyncInfo(ticket, taskInstanceId);
            asyncInfo.WaitForStatus(LongRunningTaskInstanceStatusEnum.Running, 2000);

            taskManager.Remove(ticket, taskInstanceId).Wait(2000);

            Assert.IsTrue(lst.Count == 0, "Task was not removed.");

            taskManager.StopManagerAndWait();
        }

        #endregion


        #region StopManager

        [Fact]
        public void StopManager_Success()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            MyTaskParam param = new MyTaskParam
            {
                DelayMs = 3000
            };

            string paramJson = param.ToJson();


            string title1 = "myTask1";
            var taskInstanceId1 = taskManager.Start(
                id: null,
                title: title1,
                ticket: ticket,
                paramJson: paramJson,
                taskType: new MyTaskInt().Type,
                isUpload: false,
                isDownload: false,
                order: null);

            string title2 = "myTask2";
            var taskInstanceId2 = taskManager.Start(
                id: null,
                title: title2,
                ticket: ticket,
                paramJson: paramJson,
                taskType: new MyTaskInt().Type,
                isUpload: false,
                isDownload: false,
                order: null);

            string title3 = "myTask3";
            var taskInstanceId3 = taskManager.Start(
                id: null,
                title: title3,
                ticket: ticket,
                paramJson: paramJson,
                taskType: new MyTaskInt().Type,
                isUpload: false,
                isDownload: false,
                order: null);

            Thread.Sleep(200);

            taskManager.StopManager().Wait(100);


            Assert.IsTrue(lst.Count == 3, "Repository was modified during manager shut down.");
        }

        [Fact]
        [ExpectedException(typeof(LongRunningTaskManagerStoppingException))]
        public void StopManager_StartTask_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            MyTaskParam param = new MyTaskParam
            {
                DelayMs = 3000
            };

            string paramJson = param.ToJson();

            taskManager.StopManager().Wait(100);

            string title1 = "myTask1";
            var taskInstanceId1 = taskManager.Start(
                id: null,
                title: title1,
                ticket: ticket,
                paramJson: paramJson,
                taskType: new MyTaskInt().Type,
                isUpload: false,
                isDownload: false,
                order: null);
        }

        [Fact]
        [ExpectedException(typeof(LongRunningTaskManagerStoppingException))]
        public void StopManager_GetStatus_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            MyTaskParam param = new MyTaskParam
            {
                DelayMs = 3000
            };

            string paramJson = param.ToJson();


            string title1 = "myTask1";
            var taskInstanceId1 = taskManager.Start(
                id: null,
                title: title1,
                ticket: ticket,
                paramJson: paramJson,
                taskType: new MyTaskInt().Type,
                isUpload: false,
                isDownload: false,
                order: null);

            Thread.Sleep(100);

            taskManager.StopManagerAndWait();

            Thread.Sleep(100);

            taskManager.GetStatus(ticket, taskInstanceId1);
        }

        [Fact]
        [ExpectedException(typeof(LongRunningTaskManagerStoppingException))]
        public void StopManager_GetOverallStatus_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            MyTaskParam param = new MyTaskParam
            {
                DelayMs = 3000
            };

            string paramJson = param.ToJson();


            string title1 = "myTask1";
            var taskInstanceId1 = taskManager.Start(
                id: null,
                title: title1,
                ticket: ticket,
                paramJson: paramJson,
                taskType: new MyTaskInt().Type,
                isUpload: false,
                isDownload: false,
                order: null);

            Thread.Sleep(100);

            taskManager.StopManagerAndWait();

            Thread.Sleep(100);

            taskManager.GetOverallStatus(ticket);
        }

        [Fact]
        [ExpectedException(typeof(LongRunningTaskManagerStoppingException))]
        public void StopManager_Cancel_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            MyTaskParam param = new MyTaskParam
            {
                DelayMs = 3000
            };

            string paramJson = param.ToJson();


            string title1 = "myTask1";
            var taskInstanceId1 = taskManager.Start(
                id: null,
                title: title1,
                ticket: ticket,
                paramJson: paramJson,
                taskType: new MyTaskInt().Type,
                isUpload: false,
                isDownload: false,
                order: null);

            Thread.Sleep(100);

            taskManager.StopManagerAndWait();

            Thread.Sleep(100);

            taskManager.CancelAndWait(ticket, taskInstanceId1);
        }

        [Fact]
        [ExpectedException(typeof(LongRunningTaskManagerStoppingException))]
        public void StopManager_Remove_Error()
        {
            List<LongRunningTaskInstanceSnapshot> lst = new List<LongRunningTaskInstanceSnapshot>();

            var taskManager = CreateTaskManager(lst);

            string username = "myUser";

            var ticket = CreateAuthenticationTicket(username);

            MyTaskParam param = new MyTaskParam
            {
                DelayMs = 3000
            };

            string paramJson = param.ToJson();


            string title1 = "myTask1";
            var taskInstanceId1 = taskManager.Start(
                id: null,
                title: title1,
                ticket: ticket,
                paramJson: paramJson,
                taskType: new MyTaskInt().Type,
                isUpload: false,
                isDownload: false,
                order: null);

            Thread.Sleep(100);

            taskManager.StopManagerAndWait();

            Thread.Sleep(100);

            taskManager.Remove(ticket, taskInstanceId1);
        }

        #endregion


        private ILongRunningTaskManager CreateTaskManager(List<LongRunningTaskInstanceSnapshot> lst)
        {
            // create task instance instance repository
            var taskInstanceSnapshotRepo = CreateTaskInstanceSnapshotRepo(lst);

            // create task manager
            var taskManager = CreateTaskManager(taskInstanceSnapshotRepo);

            return taskManager;
        }

        private ILongRunningTaskManager CreateTaskManager(ILongRunningTaskInstanceSnapshotRepository taskInstanceSnapshotRepo)
        {
            // file manager
            var fileManager = new DiskFileManager("TaskResult");

            // task result transformer
            var taskResultTransformer = new LongRunningTaskFileResultToFileManagerTransformer(fileManager);

            var taskManager = new LongRunningTaskManager(taskInstanceSnapshotRepo, taskResultTransformer);

            return taskManager;
        }

        private ILongRunningTaskInstanceSnapshotRepository CreateTaskInstanceSnapshotRepo(List<LongRunningTaskInstanceSnapshot> lst)
        {
            // create task instance instance repository
            var taskInstanceSnapshotRepo = new Sustainalytics.LongRunningTaskManager.Interface.Fakes.StubILongRunningTaskInstanceSnapshotRepository
            {
                GetString = (string taskInstanceId) =>
                {
                    var taskInstance = lst.Where(el => el.Id == taskInstanceId).FirstOrDefault();

                    return taskInstance;
                },

                GetAll = () =>
                {
                    return lst.OrderBy(el => el.Order).ToList();
                },

                InsertLongRunningTaskInstanceSnapshot = (LongRunningTaskInstanceSnapshot snapshot) =>
                {
                    lst.Add(snapshot);
                },

                InsertIEnumerableOfLongRunningTaskInstanceSnapshot = (IEnumerable<LongRunningTaskInstanceSnapshot> snapshots) =>
                {
                    lst.AddRange(snapshots);
                },

                DeleteString = (string taskInstanceId) =>
                {
                    var taskInstance = lst.Where(el => el.Id == taskInstanceId).FirstOrDefault();

                    if (taskInstance != null)
                    {
                        lst.Remove(taskInstance);
                    }
                }
            };

            return taskInstanceSnapshotRepo;
        }

        private AuthenticationTicket CreateAuthenticationTicket(string username)
        {
            AuthenticationTicket ticket = new AuthenticationTicket(
                identity: new ClaimsIdentity(
                    claims: new List<Claim> { new Claim(ClaimTypes.Name, username) },
                    authenticationType: "Bearer"),
                properties: new AuthenticationProperties());

            return ticket;
        }
    }
}
